package pl.multimedialne.carculator.sensors;

import android.app.Activity;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;

public class Accelerometer {
	static private Accelerometer instance = null;

	static public Accelerometer getInstance() {
		if (instance == null) {
			instance = new Accelerometer();
		}
		return instance;
	}

	private SensorManager sensorManager;
	private Float speed = 0.0f;
	private Float acceleration = 0.0f;

	private float ax, ay, az;
	private float vx, vy, vz;
	private long lastTimestamp = -1;

	private float fadeIn = 0;
	private float fadeInTime = 10;

	private Accelerometer() {
	}

	public void initialize(Activity activity) {
		sensorManager = (SensorManager) activity
				.getSystemService(Context.SENSOR_SERVICE);
		final Accelerometer that = this;
		sensorManager.registerListener(
				new SensorEventListener() {
					@Override
					public void onSensorChanged(SensorEvent event) {
						if (event.sensor.getType() == Sensor.TYPE_LINEAR_ACCELERATION) {
							if (that.lastTimestamp == -1) {
								that.lastTimestamp = event.timestamp;
								return;
							}
							float timeDelta = (event.timestamp - that.lastTimestamp) / 1000.0f / 1000.0f / 1000.0f;

							fadeIn = Math.min(fadeIn + timeDelta * 1.0f
									/ fadeInTime, 1);

							float ax = event.values[0];
							float ay = event.values[1];
							float az = event.values[2];

							float oldAx = that.ax;
							float oldAy = that.ay;
							float oldAz = that.az;

							that.ax = ax;
							that.ay = ay;
							that.az = az;

							float xArea = (oldAx + that.ax) * timeDelta * 0.5f;
							float yArea = (oldAy + that.ay) * timeDelta * 0.5f;
							float zArea = (oldAz + that.az) * timeDelta * 0.5f;
							that.vx += xArea * fadeIn;
							that.vy += yArea * fadeIn;
							that.vz += zArea * fadeIn;

							final float speedDampening = 0.5f;
							that.vx *= 1.0f - speedDampening * timeDelta;
							that.vy *= 1.0f - speedDampening * timeDelta;
							that.vz *= 1.0f - speedDampening * timeDelta;

							setSpeed((float) Math.sqrt(that.vx * that.vx
									+ that.vy * that.vy + that.vz * that.vz) * 3.6f);
							setAcceleration((float) Math.sqrt(that.ax * that.ax
									+ that.ay * that.ay + that.az * that.az));

							that.lastTimestamp = event.timestamp;
						}
					}

					@Override
					public void onAccuracyChanged(Sensor sensor, int accuracy) {
					}
				}, sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
				SensorManager.SENSOR_DELAY_FASTEST);
	}

	private void setSpeed(float speed) {
		synchronized (this.speed) {
			this.speed = speed;
		}
	}

	public float getSpeed() {
		synchronized (this.speed) {
			return speed;
		}
	}

	public Float getAcceleration() {
		synchronized (this.acceleration) {
			return acceleration;
		}
	}

	private void setAcceleration(Float acceleration) {
		synchronized (this.acceleration) {
			this.acceleration = acceleration;
		}
	}
}
